package com.cmpe451.nutty.controller;

import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.cmpe451.nutty.model.Comment;
import com.cmpe451.nutty.model.Favorite;
import com.cmpe451.nutty.model.Follow;
import com.cmpe451.nutty.model.Ingredients;
import com.cmpe451.nutty.model.Menu;
import com.cmpe451.nutty.model.Menu_Recipe;
import com.cmpe451.nutty.model.Rate;
import com.cmpe451.nutty.model.Recipe;
import com.cmpe451.nutty.model.Recipe_Derive;
import com.cmpe451.nutty.model.Recipe_Tag;
import com.cmpe451.nutty.model.Role;
import com.cmpe451.nutty.model.Style;
import com.cmpe451.nutty.model.Unit;
import com.cmpe451.nutty.model.User;
import com.cmpe451.nutty.service.MailService;
import com.cmpe451.nutty.service.RecipeService;
import com.cmpe451.nutty.service.TestService;
import com.cmpe451.nutty.service.UserService;
import com.cmpe451.nutty.so.GuiUser;
import com.cmpe451.nutty.dao.UserDao;
/**
 * The recipe controller class is the controller of main recipe functions
 * @author CmpE Group 2 Fall 2014 nuttyconfirmation@gmail.com
 * @version 2.0, January 2015
 */
@Controller
@RequestMapping(value = "/recipe")
public class RecipeController extends HomeController {
	@Autowired
	private TestService twitterService;

	@Autowired
	private UserService userDetailsService;

	@Autowired
	private RecipeService recipeService;

	@Autowired
	private MailService mailService;

	@Autowired
	private UserDao userDao;
	/*
	 * to be changed for deployment need to create an empty folder on server
	 */
	
	//private String recipePhotoDirectory =
	 //"/Users/alper/Desktop/recipeImages/"; private String
	 //profilePhotoDirectory ="/Users/alper/Desktop/profileImages/";
	 
	/**
	 * @param recipePhotoDirectory the photo directory of recipe on server
	 * @param profilePhotoDirectory the photo directory of profile on server
	 */
	private String recipePhotoDirectory = "/home/project2/nutty/recipeImages/";
	private String profilePhotoDirectory = "/home/project2/nutty/profileImages/";
	
	//private String recipePhotoDirectory =
	  //"/Users/alper/Desktop/recipeImages/"; private String
	  //profilePhotoDirectory ="/Users/alper/Desktop/profileImages/";
	/**
	 * This function fills the createRecipe.jsp with encessary attributes.
	 * @param model incoming model from createRecipe.jsp form
	 * @param recipeId id of the recipe if the recipe is going to be edited.
	 * @return redirects user to the createRecipe.view, tiles definition, bootstrapped version
	 */
	@RequestMapping(value = "/createRecipe")
	public Object createRecipe(Model model,
			@RequestParam(required = false) Long recipeId) {
		if(getUser() == null){
			return "login.view";
		}
		model.addAttribute("user", getUser());
		List<Ingredients> ingredientList = recipeService.getIngredients();
		List<Unit> unitList = recipeService.getUnits();
		model.addAttribute("units", unitList);
		List<Style> styleList = recipeService.getStyles();
		model.addAttribute("styles", styleList);
		if (recipeId != null) {

			// exception handle etmeyi unutma
			Recipe recipe = recipeService.getRecipeById(recipeId);
			model.addAttribute("recipename", recipe.getRecipe_name());
			model.addAttribute("recipedescription", recipe.getDescription());
			String[] list = amountParser(recipe.getIngredients_list(), 1);

			double calory = 0;
			for (String s : list) {
				System.out.println("tek tek: " + s);

			}
			model.addAttribute("recipecalory", calory);
			System.out.println(Arrays.toString(list));
			model.addAttribute("ingredientlist", list);

		}

		// List<Recipe> list = recipeService.
		// too tired, need to write a criteria in dao to access user's recipes
		// model.addAttribute("texts", list);
		model.addAttribute("ingredients", ingredientList);
		return "createRecipe.view";

	}

	/**
	 * This function derives recipes from a current recipe.
	 * @param model incoming model of recipe.jsp
	 * @param recipeId the id of the current recipe
	 * @return redirects user to the deriverecipe.view, tiles definition, bootstrapped verison
	 */
	@RequestMapping(value = "/deriveRecipe", method = RequestMethod.GET)
	public Object deriveRecipe(Model model,
			@RequestParam(required = true) Long recipeId) {
		if(getUser() == null){
			return "login.view";
		}
		model.addAttribute("user", getUser());


		List<Ingredients> ingredientList = recipeService.getIngredients();
		List<Unit> unitList = recipeService.getUnits();
		model.addAttribute("units", unitList);
		List<Style> styleList = recipeService.getStyles();
		model.addAttribute("styles", styleList);
		if (recipeId != null) {

			// exception handle etmeyi unutma
			Recipe recipe = recipeService.getRecipeById(recipeId);
			model.addAttribute("recipename", recipe.getRecipe_name());
			model.addAttribute("recipedescription", recipe.getDescription());
			String[] list = amountParser(recipe.getIngredients_list(), 1);
			System.out.println(Arrays.toString(list));
			model.addAttribute("ingredientlist", list);

		}
		// List<Recipe> list = recipeService.
		// too drunk, need to write a criteria in dao to access user's recipes
		// model.addAttribute("texts", list);
		model.addAttribute("ingredients", ingredientList);

		return "deriveRecipe.view";

	}

	/**
	 * This functions saves comments to the database.
	 * @param model incoming model from saveComment form
	 * @param recipeId id of the current recipe
	 * @param comment content of the comment to be saved
	 * @return redirects user to the current recipe page
	 */
	@RequestMapping(value = "/saveComment")
	public Object saveComment(Model model,
			@RequestParam(required = true) Long recipeId,
			@RequestParam(required = true) String comment) {
		if(getUser() == null){
			return "login.view";
		}
		Comment newComment = new Comment();

		newComment.setComment(comment);
		newComment.setRecipe_id(recipeId);
		newComment.setUser(getUser().getUsername());
		newComment.setUserid(getUser().getId());
		newComment.setLikes(0L);
		newComment.setDislikes(0L);
		recipeService.saveComment(newComment);
		User user = userDetailsService.getUserById(getUser().getId());
		user.setLevel_point(user.getLevel_point() + 10L);
		userDetailsService.updateUser(user);
		return "redirect:/recipe/recipeInfo?recipeId=" + recipeId;

	}

	/**
	 * This function rates recipes between 1 and 5
	 * @param model incoming model from rateRecipe form
	 * @param rate the value of rating
	 * @param recipeId the id of recipe going to be rated
	 * @return redirects user to the current recipe page
	 */
	@RequestMapping(value = "/rateRecipe")
	public Object rateRecipe(Model model,
			@RequestParam(required = true) int rate,
			@RequestParam(required = true) Long recipeId) {
		if(getUser() == null){
			return "login.view";
		}

		Recipe recipe = recipeService.getRecipeById(recipeId);
		List<Rate> ratesofaRecipe = recipeService.getTotalRate(recipe);
		GuiUser user = getUser();
		User aUser = userDetailsService.getUserById(user.getId());
		String currentUsername = aUser.getUsername();
		Rate oldRate = new Rate();
		for (Rate r : ratesofaRecipe) {
			if (r.getUsername().equals(getUser().getUsername()))
				oldRate = r;
		}
		if (oldRate.getUsername() != null) {
			oldRate.setRate(rate);
			recipeService.updateRate(oldRate);
		} else {
			Rate newRate = new Rate();
			newRate.setRate(rate);
			newRate.setRecipe_id(recipeId);
			newRate.setUsername(currentUsername);
			aUser.setLevel_point(user.getLevel_point() + 5L);
			userDetailsService.updateUser(aUser);
			recipeService.saveRate(newRate);
		}

		List<Rate> ratesofaRecipeNew = recipeService.getTotalRate(recipe);
		int sum = 0;
		for (Rate r : ratesofaRecipeNew) {
			sum += r.getRate();
		}
		int totalCount = ratesofaRecipeNew.size();
		recipe.setRating_count(totalCount);
		recipe.setRate((double) sum / totalCount);
		recipe.setTotal_rating((long)sum);
		recipeService.updateRecipe(recipe);

		return "redirect:/recipe/recipeInfo?recipeId=" + recipeId;
	}
	
	/**
	 * This function saves recipe as favorite.
	 * @param model incoming model from favRecipe form
	 * @param recipeId id of the current recipe
	 * @return redirects user to the current recipe page
	 */
	@RequestMapping(value = "/favRecipe")
	public Object favRecipe(Model model,
			@RequestParam(required = true) Long recipeId) {
		if(getUser() == null){
			return "login.view";
		}

		GuiUser user = getUser();
		
		User aUser = userDetailsService.getUserById(user.getId());
		
		Favorite thisFav = new Favorite();
		thisFav.setUserId(aUser.getId());
		thisFav.setRecipeId(recipeId);

		recipeService.saveFav(thisFav);

		return "redirect:/recipe/recipeInfo?recipeId=" + recipeId;
	}
	
	/**
	 * This function unfavorites the given recipe
	 * @param model incoming unfavRecipe model
	 * @param recipeId the id of the current recipe
	 * @return redirects user to the current recipe page
	 */
	@RequestMapping(value = "/unfavRecipe")
	public Object unfavRecipe(Model model,
			@RequestParam(required = true) Long recipeId) {
		if(getUser() == null){
			return "login.view";
		}

		GuiUser user = getUser();
		
		
		Favorite thisFav = recipeService.getFav(user.getId(), recipeId);
		recipeService.deleteFav(thisFav);

		return "redirect:/recipe/recipeInfo?recipeId=" + recipeId;
	}

	/**
	 * This function get the picture of the given recipe
	 * @param id the of the recipe
	 * @param response Http request
	 */
	@RequestMapping(value = "/picture/{id}", method = RequestMethod.GET)
	public void getRecipePicture(@PathVariable String id,
			HttpServletResponse response) {
		System.out.println("response: " + response.toString());
		try {
			InputStream in = new FileInputStream(new File(recipePhotoDirectory
					+ id + ".png"));

			IOUtils.copy(in, response.getOutputStream());
			response.flushBuffer();
		} catch (Exception e) {
			logger.error("Can't get recipe picture due to {0}", e.getMessage());
			logger.error("File path: " + recipePhotoDirectory + id + ".png");
		}
	}

	/**
	 * This function displays the basic info on a recipe.
	 * @param model incoming model from recipe.jsp
	 * @param recipeId current id of the recipe
	 * @param request Http request
	 * @return redirects user to the recipe.view, tiles definition, bootstrapped version
	 */
	@RequestMapping(value = "/recipeInfo")
	public Object recipe(Model model,
			@RequestParam(required = true) Long recipeId,
			HttpServletRequest request) {

		String currentUrl = makeUrl(request);
		model.addAttribute("currentUrl", currentUrl);
		System.out.println("File path: " + recipePhotoDirectory + recipeId
				+ ".png");
		String derivationUrl = currentUrl.replace("recipeInfo", "deriveRecipe");
		model.addAttribute("derivationUrl", derivationUrl);
		model.addAttribute("user", getUser());
		GuiUser user = getUser();
		Recipe recipe = recipeService.getRecipeById(recipeId);
		Favorite fav = new Favorite();
		if(getUser()!=null)
			fav = recipeService.getFav(user.getId(), recipeId);
		if(fav != null)
			model.addAttribute("isFavorited", true);
		else
			model.addAttribute("isFavorited", false);
		
		if (user != null) {
			User aUser = userDetailsService.getUserById(user.getId());
			if (aUser != null) {
				Rate rate = recipeService.findRatebyRecipe(aUser.getUsername(),
						recipeId);
				if (rate != null)
					model.addAttribute("rateOnRecipe", rate.getRate());
				else
					model.addAttribute("rateOnRecipe", 0);
			}
		}

		List<Recipe_Derive> derivedRecipes = recipeService.findDerivedRecipesById(recipeId);
		model.addAttribute("derivedRecipes", derivedRecipes);
		model.addAttribute("carbs", recipe.getCarbs());
		model.addAttribute("fat", recipe.getFat());
		model.addAttribute("protein", recipe.getProtein());
		model.addAttribute("calory", recipe.getCalory());
		List<Comment> recipeComments = recipeService.getCommentsById(recipeId);
		model.addAttribute("comments", recipeComments);
		model.addAttribute("recipeowner", recipe.getRecipe_owner());
		model.addAttribute("recipename", recipe.getRecipe_name());
		model.addAttribute("recipedescription", recipe.getDescription());
		model.addAttribute("recipeId", recipeId);
		model.addAttribute("cooktime", recipe.getCook_time());
		model.addAttribute("preptime", recipe.getPrep_time());
		model.addAttribute("totaltime", recipe.getCook_time() + recipe.getPrep_time());
		String[] list = amountParser(recipe.getIngredients_list(), 1);
		model.addAttribute("ingredientlist", list);
		model.addAttribute("rating",recipe.getRate());
		model.addAttribute("ratingcount",recipe.getRating_count());
		
		return "recipe.view";

	}

	/**
	 * 
	 * This function displays the create menu page.
	 * @param model incoming model for createMenu form
	 * @return createMenu.view, tiles definition, bootstrapped version
	 */
	@RequestMapping(value = "/createMenu")
	public Object createMenucreateRecipe(Model model) {
		/*if(getUser() == null){
			return "login.view";
		}*/
		List<Recipe> userRecipes = recipeService.getRecipes(getUser());
		model.addAttribute("recipes", userRecipes);

		return "createMenu.view";

	}

	/**
	 * This function saves menu to the database
	 * @param name the name of the menu
	 * @param mydata lists of recipes to be included into menu
	 * @return redirects user to the recipe page
	 */
	@RequestMapping(value = "/saveMenu")
	public Object saveMenu(@RequestParam String name,
			@RequestParam String mydata) {
		if(getUser() == null){
			return "login.view";
		}
		System.out.println("name: " + name);
		System.out.println("recipeList: " + mydata);
		String[] recipeList = mydata.split(",");
		
		Menu m = new Menu();
		m.setName(name);
		m.setUserId(getUser().getId());
		recipeService.saveMenu(m);
		
		for(String s: recipeList){
			Long recipeId = Long.parseLong(s);
			Menu_Recipe menuRec = new Menu_Recipe();
			menuRec.setMenuId(m.getId());
			menuRec.setRecipeId(recipeId);
			recipeService.saveMenu_Recipe(menuRec);
		}
		return "redirect:/recipe/menu?success=true";
	}

	/**
	 * This function saves recipe to the database.
	 * @param mydata includes ingredients.
	 * @param description the description of the recipe
	 * @param name the name of the recipe
	 * @param recipeId the id of the recipe
	 * @param easiness the easiness of the recipe
	 * @param cookTime the cooktime of the recipe
	 * @param cost the cost of the recipe
	 * @param taglist the tag list of the recipe
	 * @param file the recipe image
	 * @return redirects user to the recipe page
	 */
	@RequestMapping(value = "/saveRecipe")
	public Object saveRecipe(@RequestParam String mydata,
			@RequestParam String description, @RequestParam String name,
			@RequestParam(required = false) Long recipeId,
			@RequestParam String easiness, @RequestParam Integer cookTime,
			@RequestParam Long cost, @RequestParam String taglist,
			@RequestParam(value = "file", required = false) MultipartFile file) {
		if(getUser() == null){
			return "login.view";
		}

		GuiUser user = getUser();
		Recipe recipe;

		double totalCarb = 0;
		double totalCalory = 0;
		double totalFat = 0;
		double totalProtein = 0;
		String[] ingredientList = mydata.split(",");
		for(String ingredient: ingredientList){
			String[] pieces = ingredient.split("/");
			Ingredients newIngredient = recipeService.getIngredientByName(pieces[2]);
			if(newIngredient != null){
				totalCarb += newIngredient.getCarbs();
				totalCalory += newIngredient.getCalory();
				totalFat += newIngredient.getFat();
				totalProtein += newIngredient.getProtein();
			}
		}
		if (recipeId == null) {
			recipe = new Recipe();			
			recipe.setRecipe_owner(user.getUsername());
			recipe.setRate(0.0);
			recipe.setRecipe_name(name);
			recipe.setDescription(description);
			recipe.setIngredients_list(mydata);
			recipe.setEasiness(easiness);
			recipe.setRating_count(0);
			recipe.setTotal_rating(0L);
			recipe.setCost(cost);
			recipe.setCalory((double)Math.round(totalCalory));
			recipe.setCarbs((double)Math.round(totalCarb));
			recipe.setFat((double)Math.round(totalFat));
			recipe.setProtein((double)Math.round(totalProtein));
			recipe.setEasiness(easiness);
			recipe.setCook_time(cookTime);
			recipeService.saveRecipe(recipe);
		} else {

			recipe = recipeService.getRecipeById(recipeId);
			recipe.setRecipe_name(name);
			recipe.setDescription(description);
			recipe.setIngredients_list(mydata);
			recipe.setCalory((double)Math.round(totalCalory));
			recipe.setCarbs((double)Math.round(totalCarb));
			recipe.setFat((double)Math.round(totalFat));
			recipe.setProtein((double)Math.round(totalProtein));
			recipe.setCalory(totalCalory);
			recipe.setCarbs(totalCarb);
			recipe.setFat(totalFat);
			recipe.setProtein(totalProtein);
			recipe.setEasiness(easiness);
			recipe.setCook_time(cookTime);
			recipe.setCost(cost);
			recipeService.updateRecipe(recipe);
			// update

		}

		if (file != null && file.getSize() > 0) {
			try {
				String temp = Objects.toString(recipe.getId(), null);
				String fileName = saveRecipeFile(file, temp);
				recipe.setImage_link(fileName);
				recipeService.updateRecipe(recipe);
			} catch (IllegalStateException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		String[] tags = taglist.split(" ");
		for (String tag : tags) {
			Recipe_Tag rt = new Recipe_Tag();
			rt.setRecipe_id(recipe.getId());
			rt.setTag(tag);
			recipeService.addTag(rt);
		}
		User myuser = userDetailsService.getUserById(getUser().getId());
		myuser.setLevel_point(user.getLevel_point() + 50L);
		userDetailsService.updateUser(myuser);
		return "redirect:/recipe/createRecipe?success=true";
	}

	/**
	 * This function saves derived recipe.
	 * @param mydata includes ingredients.
	 * @param description the description of the recipe
	 * @param name the name of the recipe
	 * @param recipeId the id of the recipe
	 * @param easiness the easiness of the recipe
	 * @param cookTime the cooktime of the recipe
	 * @param cost the cost of the recipe
	 * @param taglist the tag list of the recipe
	 * @param file the recipe image
	 * @return redirects user to the recipe page
	 */
	@RequestMapping(value = "/saveDerivedRecipe")
	public Object saveDerivedRecipe(@RequestParam String mydata,
			@RequestParam String description, @RequestParam String name,
			@RequestParam(required = false) Long recipeId,
			@RequestParam String easiness, @RequestParam Integer cookTime,
			@RequestParam Long cost,
			@RequestParam(value = "file", required = false) MultipartFile file) {
		if(getUser() == null){
			return "login.view";
		}

		GuiUser user = getUser();
		Recipe recipe;
		double totalCarb = 0;
		double totalCalory = 0;
		double totalFat = 0;
		double totalProtein = 0;
		String[] ingredientList = mydata.split(",");
		for(String ingredient: ingredientList){
			String[] pieces = ingredient.split("/");
			Ingredients newIngredient = recipeService.getIngredientByName(pieces[2]);
			if(newIngredient != null){
				totalCarb += newIngredient.getCarbs();
				totalCalory += newIngredient.getCalory();
				totalFat += newIngredient.getFat();
				totalProtein += newIngredient.getProtein();
			}
		}
		System.out.println(totalCarb);
		System.out.println(totalCalory);
		System.out.println(totalFat);
		System.out.println(totalProtein);
		Recipe newRecipe = new Recipe();
		if (recipeId == null) {
			return "home.view";
		} else {
			recipe = recipeService.getRecipeById(recipeId);
			System.out.println("recipe descr: " + recipe.getDescription());
			System.out.println("page descrip: " + description);
			for (int i = 0; i < recipe.getDescription().length() - 1; i++) {
				if (description.charAt(i) != recipe.getDescription().charAt(i))
					System.out.println("these are different: "
							+ description.charAt(i) + " and "
							+ recipe.getDescription().charAt(i));
			}
			if (recipe.getDescription().equalsIgnoreCase(description)) {
				return "redirect:/recipe/deriveRecipe?success=false&recipeId="
						+ recipeId;
			} else {
				System.out.println("changed!");

				newRecipe.setRecipe_owner(user.getUsername());
				newRecipe.setRecipe_name(name);
				newRecipe.setDescription(description);
				newRecipe.setIngredients_list(mydata);
				newRecipe.setEasiness(easiness);
				newRecipe.setRating_count(0);
				newRecipe.setCalory((double)Math.round(totalCalory));
				newRecipe.setCarbs((double)Math.round(totalCarb));
				newRecipe.setFat((double)Math.round(totalFat));
				newRecipe.setProtein((double)Math.round(totalProtein));
				newRecipe.setTotal_rating(0L);
				newRecipe.setCost(cost);
				newRecipe.setRate(0.0);
				newRecipe.setEasiness(easiness);
				newRecipe.setCook_time(cookTime);
				User myuser = userDetailsService.getUserById(getUser().getId());
				myuser.setLevel_point(user.getLevel_point() + 20L);
				userDetailsService.updateUser(myuser);
				recipeService.saveRecipe(newRecipe);
			}

			if (file != null && file.getSize() > 0) {
				try {
					String temp = Objects.toString(newRecipe.getId(), null);
					String fileName = saveRecipeFile(file, temp);
					recipe.setImage_link(fileName);
					recipeService.updateRecipe(newRecipe);
				} catch (IllegalStateException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		Recipe_Derive newRecipe_Derive = new Recipe_Derive();
		newRecipe_Derive.setCurrent_recipe(recipeId);
		newRecipe_Derive.setDerived_recipe(newRecipe.getId());
		newRecipe_Derive.setDerived_recipe_name(newRecipe.getRecipe_name());
		recipeService.saveDerivedRecipe(newRecipe_Derive);

		return "redirect:/recipe/recipeInfo?recipeId=" + newRecipe.getId();
	}
	
	/**
	 * This function displays the menu page
	 * @param model incoming model of the menu.jsp
	 * @return menu.view, tiles definition, bootstrapped version
	 */
	@RequestMapping(value = "/viewMenu")
	 public Object menu(Model model) {
	 	if(getUser() == null){
	 	return "login.view";
	 	}
	 	List<Menu> userMenus = recipeService.getMenusOfUser(getUser().getId());
	 	for(Menu m : userMenus){
	 	List<Menu_Recipe> menuRecipeList = recipeService.getMenuRecipePairs(m.getId());
	 	List<Recipe> recipeList = new ArrayList<Recipe>();
	 	for(Menu_Recipe menuRec: menuRecipeList){
	 	recipeList.add(recipeService.getRecipeById(menuRec.getRecipeId()));
	 	}
	 	model.addAttribute("recipesOf"+m.getId(), recipeList);
	 	}
	 	System.out.println("menulist size: "+userMenus.size());
	 	model.addAttribute("menuList", userMenus);

	 	return "viewMenu.view";

	 }
}
